package com.huahai.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huahai.common.DeleteRequest;
import com.huahai.constant.RedisConstant;
import com.huahai.exception.BusinessException;
import com.huahai.exception.ErrorCode;
import com.huahai.mapper.CommentMapper;
import com.huahai.pojo.dto.comment.CommentAddDTO;
import com.huahai.pojo.dto.comment.CommentQueryDTO;
import com.huahai.pojo.entity.Comment;
import com.huahai.pojo.entity.Picture;
import com.huahai.pojo.entity.User;
import com.huahai.pojo.vo.comment.CommentVO;
import com.huahai.pojo.vo.user.UserVO;
import com.huahai.service.CommentService;
import com.huahai.service.PictureService;
import com.huahai.service.UserService;
import com.huahai.util.ThrowUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author huahai
 * @description 针对表【comment(评论)】的数据库操作Service实现
 * @createDate 2025-08-26 15:32:53
 */
@Service
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements CommentService {

    @Resource
    private UserService userService;

    @Resource
    private CommentMapper commentMapper;

    @Resource
    private PictureService pictureService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    // 评论点赞数缓存 key
    private final String COMMENT_LIKE_COUNT_KEY = RedisConstant.PROJECT_NAME + RedisConstant.COMMENT_LIKE_COUNT;

    // 评论点赞列表缓存 key
    private final String COMMENT_LIKE_LIST_KEY = RedisConstant.PROJECT_NAME + RedisConstant.COMMENT_LIKED_USER_LIST;

    /**
     * 添加评论
     *
     * @param commentAddDTO 添加评论参数
     * @param loginUser     登录用户
     * @return Long 添加的评论id
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long addComment(CommentAddDTO commentAddDTO, User loginUser) {
        // 校验参数
        ThrowUtils.throwIf(commentAddDTO == null, ErrorCode.PARAMS_ERROR);
        validAddComment(commentAddDTO);
        // 添加评论到数据库
        Comment comment = new Comment();
        BeanUtils.copyProperties(commentAddDTO, comment);
        Long pictureId = comment.getPictureId();
        Picture picture = pictureService.getById(pictureId);
        ThrowUtils.throwIf(picture == null, ErrorCode.NOT_FOUND_ERROR, "图片不存在");
        comment.setUserId(loginUser.getId());
        boolean saveResult = this.save(comment);
        ThrowUtils.throwIf(!saveResult, ErrorCode.OPERATION_ERROR, "评论失败");
        // 修改图片的评论数
        boolean updateResult = pictureService.update()
                .eq("id", pictureId)
                .set("commentCount", picture.getCommentCount() + 1)
                .update();
        ThrowUtils.throwIf(!updateResult, ErrorCode.OPERATION_ERROR, "更新图片评论数失败");
        // 返回添加的评论 id
        return comment.getId();
    }

    /**
     * 删除评论
     *
     * @param deleteRequest 删除参数
     * @param loginUser     登录用户
     * @return Boolean
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean deleteComment(DeleteRequest deleteRequest, User loginUser) {
        // 校验参数
        ThrowUtils.throwIf(deleteRequest == null, ErrorCode.PARAMS_ERROR);
        Long commentId = deleteRequest.getId();
        ThrowUtils.throwIf(commentId == null || commentId <= 0, ErrorCode.PARAMS_ERROR);
        Comment comment = this.getById(commentId);
        ThrowUtils.throwIf(comment == null, ErrorCode.NOT_FOUND_ERROR, "评论不存在");
        // 校验是否具有删除的权限
        Long userId = comment.getUserId();
        if (!userId.equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        // 使用 SQL 递归查询获取所有需要删除的评论 ID（当前评论 + 所有后代评论）
        List<Long> allCommentIds = commentMapper.getAllDescendantCommentIds(commentId);
        // 批量删除所有评论
        boolean deleteResult = this.removeBatchByIds(allCommentIds);
        ThrowUtils.throwIf(!deleteResult, ErrorCode.OPERATION_ERROR, "删除失败");
        // 批量更新图片的评论数
        Long pictureId = comment.getPictureId();
        boolean updateResult = pictureService.lambdaUpdate()
                .eq(Picture::getId, pictureId)
                .setSql("commentCount = commentCount - " + allCommentIds.size())
                .update();
        ThrowUtils.throwIf(!updateResult, ErrorCode.OPERATION_ERROR, "更新图片评论数失败");

        // TODO 删除所有图片关联的点赞数据

        return true;
    }

    /**
     * 根据 id 获取单条评论 (管理员)
     *
     * @param commentQueryDTO 评论查询参数
     * @param loginUser       登录用户
     * @return CommentVO
     */
    @Override
    public Comment getCommentById(CommentQueryDTO commentQueryDTO, User loginUser) {
        // 校验参数
        validQueryComment(commentQueryDTO);
        Long commentId = commentQueryDTO.getId();
        // 查询评论
        Comment comment = this.getById(commentId);
        ThrowUtils.throwIf(comment == null, ErrorCode.NOT_FOUND_ERROR, "评论不存在");
        // 获取当前登录用户的点赞信息
        Boolean isLike = checkCommentLiked(CommentVO.objToVo(comment), loginUser);
        comment.setIsLike(isLike);
        // 返回评论
        return comment;
    }

    /**
     * 根据 id 获取单条评论 (脱敏)
     *
     * @param commentQueryDTO 评论查询参数
     * @param loginUser       登录用户
     * @return CommentVO
     */
    @Override
    public CommentVO getCommentVOById(CommentQueryDTO commentQueryDTO, User loginUser, HttpServletRequest request) {
        // 校验参数
        validQueryComment(commentQueryDTO);
        Long commentId = commentQueryDTO.getId();
        // 查询评论
        Comment comment = this.getById(commentId);
        ThrowUtils.throwIf(comment == null, ErrorCode.NOT_FOUND_ERROR, "评论不存在");
        // 获取当前登录用户的点赞信息
        Boolean isLike = checkCommentLiked(CommentVO.objToVo(comment), loginUser);
        comment.setIsLike(isLike);
        // 封装评论
        return this.getCommentVO(comment, request);
    }

    /**
     * 分页获取评论列表 （管理员）
     *
     * @param commentQueryDTO 分页参数
     * @param loginUser       登录用户
     * @return Page<Comment> 分页后的评论列表
     */
    @Override
    public Page<Comment> getCommentPage(CommentQueryDTO commentQueryDTO, User loginUser) {
        // 1. 校验参数
        validQueryComment(commentQueryDTO);
        // 2. 获取分页参数
        long current = commentQueryDTO.getCurrent();
        long size = commentQueryDTO.getPageSize();
        // 3. 查询数据库
        Page<Comment> commentPage = this.page(new Page<>(current, size),
                this.getQueryWrapper(commentQueryDTO));
        // 填充评论信息（点赞数，是否点赞），优先从 redis 里获取点赞数和点赞用户列表
        fillCommentInfoFromRedis(commentPage, loginUser);
        // 4. 返回不脱敏的评论信息列表
        return commentPage;
    }

    /**
     * 分页获取评论列表 （脱敏）
     *
     * @param commentQueryDTO 分页参数
     * @param loginUser       登录用户
     * @return Page<CommentVO> 分页后的评论列表
     */
    @Override
    public Page<CommentVO> getCommentVOPage(CommentQueryDTO commentQueryDTO, User loginUser, HttpServletRequest request) {
        // 1. 校验参数
        validQueryComment(commentQueryDTO);
        // 2. 获取分页参数
        long current = commentQueryDTO.getCurrent();
        long size = commentQueryDTO.getPageSize();
        // 3. 查询数据库
        Page<Comment> commentPage = this.page(new Page<>(current, size),
                this.getQueryWrapper(commentQueryDTO));
        // 填充评论信息（点赞数，是否点赞），优先从 redis 里获取点赞数和点赞用户列表
        fillCommentInfoFromRedis(commentPage, loginUser);
        // 4. 返回脱敏的评论信息列表
        return this.getCommentVOPage(commentPage, request);
    }

    /**
     * comment类型转换脱敏（单个评论）
     *
     * @param comment 评论对象
     * @param request 请求
     * @return CommentVO
     */
    @Override
    public CommentVO getCommentVO(Comment comment, HttpServletRequest request) {
        // 对象转封装类
        CommentVO commentVO = CommentVO.objToVo(comment);
        // 关联查询用户信息
        Long userId = commentVO.getUserId();
        if (userId != null && userId > 0) {
            User user = userService.getById(userId);
            UserVO userVO = userService.getUserVO(user);
            commentVO.setUser(userVO);
        }
        return commentVO;
    }

    /**
     * 分页获取评论封装
     *
     * @param commentPage 分页参数
     * @param request     请求
     * @return Page<CommentVO> 分页后的脱敏评论列表
     */
    @Override
    public Page<CommentVO> getCommentVOPage(Page<Comment> commentPage, HttpServletRequest request) {
        // 获取分页数据
        List<Comment> commentList = commentPage.getRecords();
        Page<CommentVO> commentVOPage = new Page<>(commentPage.getCurrent(), commentPage.getSize(), commentPage.getTotal());
        if (CollUtil.isEmpty(commentList)) {
            return commentVOPage;
        }
        // 对象列表 => 封装对象列表
        List<CommentVO> commentVOList = commentList.stream().map(CommentVO::objToVo).collect(Collectors.toList());
        // 1. 关联查询用户信息
        // 获取用户id列表
        Set<Long> userIdSet = commentList.stream().map(Comment::getUserId).collect(Collectors.toSet());
        Map<Long, List<User>> userIdUserListMap = userService.listByIds(userIdSet).stream()
                .collect(Collectors.groupingBy(User::getId));
        // 2. 填充信息
        commentVOList.forEach(commentVO -> {
            Long userId = commentVO.getUserId();
            User user = null;
            if (userIdUserListMap.containsKey(userId)) {
                user = userIdUserListMap.get(userId).get(0);
            }
            commentVO.setUser(userService.getUserVO(user));
        });
        commentVOPage.setRecords(commentVOList);
        // 3. 返回数据
        return commentVOPage;
    }

    /**
     * 封装评论查询条件QueryWrapper
     *
     * @param commentQueryDTO 评论查询条件
     * @return QueryWrapper<Comment>
     */
    @Override
    public QueryWrapper<Comment> getQueryWrapper(CommentQueryDTO commentQueryDTO) {
        QueryWrapper<Comment> queryWrapper = new QueryWrapper<>();
        if (commentQueryDTO == null) {
            return queryWrapper;
        }
        // 从对象中取值
        Long id = commentQueryDTO.getId();
        Long userId = commentQueryDTO.getUserId();
        String content = commentQueryDTO.getContent();
        Long fatherId = commentQueryDTO.getFatherId();
        Long pictureId = commentQueryDTO.getPictureId();
        Integer likeCount = commentQueryDTO.getLikeCount();
        String sortField = commentQueryDTO.getSortField();
        String sortOrder = commentQueryDTO.getSortOrder();

        // 封装查询条件
        queryWrapper.eq(ObjUtil.isNotEmpty(id), "id", id);
        queryWrapper.eq(ObjUtil.isNotEmpty(userId), "userId", userId);
        queryWrapper.like(StrUtil.isNotBlank(content), "content", content);
        queryWrapper.eq(ObjUtil.isNotEmpty(fatherId), "fatherId", fatherId);
        queryWrapper.eq(ObjUtil.isNotEmpty(pictureId), "pictureId", pictureId);
        queryWrapper.eq(ObjUtil.isNotEmpty(likeCount), "likeCount", likeCount);

        // 排序
        queryWrapper.orderBy(StrUtil.isNotEmpty(sortField), sortOrder.equals("ascend"), sortField);
        return queryWrapper;
    }

    /**
     * 添加评论基本校验
     *
     * @param commentAddDTO 评论对象
     */
    @Override
    public void validAddComment(CommentAddDTO commentAddDTO) {
        // 校验参数
        ThrowUtils.throwIf(commentAddDTO == null, ErrorCode.PARAMS_ERROR);
        String content = commentAddDTO.getContent();
        if (StrUtil.isBlank(content)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "内容不能为空");
        }
        if (content.length() > 64) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "内容过长");
        }
    }

    /**
     * 校验查询评论参数
     *
     * @param commentQueryDTO
     * @return
     */
    @Override
    public void validQueryComment(CommentQueryDTO commentQueryDTO) {
        // 校验参数
        ThrowUtils.throwIf(commentQueryDTO == null, ErrorCode.PARAMS_ERROR);
        Long commentId = commentQueryDTO.getId();
        ThrowUtils.throwIf(commentId != null && commentId <= 0, ErrorCode.PARAMS_ERROR);
        String content = commentQueryDTO.getContent();
        ThrowUtils.throwIf(StrUtil.isNotBlank(content) && content.length() > 64, ErrorCode.PARAMS_ERROR);
        Long fatherId = commentQueryDTO.getFatherId();
        if (fatherId != null) {
            ThrowUtils.throwIf(fatherId <= 0, ErrorCode.PARAMS_ERROR);
        }
        Long pictureId = commentQueryDTO.getPictureId();
        ThrowUtils.throwIf(pictureId != null && pictureId <= 0, ErrorCode.PARAMS_ERROR);
    }

    /**
     * 检查评论是否被当前用户点赞
     *
     * @param commentVO 评论信息
     * @param loginUser 登录用户
     * @return 是否被当前用户点赞
     */
    @Override
    public Boolean checkCommentLiked(CommentVO commentVO, User loginUser) {
        // 从 redis 获取当前登录用户的评论点赞信息
        return stringRedisTemplate.opsForSet()
                .isMember(COMMENT_LIKE_LIST_KEY + commentVO.getId(), loginUser.getId().toString());
    }

    /**
     * 填充图片信息从 Redis 缓存（点赞数、是否点赞）
     *
     * @param page      评论分页数据
     * @param loginUser 当前登录用户，用于判断是否点赞
     */
    public void fillCommentInfoFromRedis(Page<Comment> page, User loginUser) {
        if (loginUser == null) {
            return;
        }
        // 遍历分页中的每条图片记录，填充缓存中的浏览量、点赞数和是否点赞状态
        for (Comment comment : page.getRecords()) {
            Long commentId = comment.getId();
            if (commentId == null || commentId <= 0) {
                continue;
            }
            // 从 Redis 中获取点赞数并设置到记录中
            String likeCountStr = stringRedisTemplate.opsForValue().get(COMMENT_LIKE_COUNT_KEY + commentId);
            if (StrUtil.isNotBlank(likeCountStr)) {
                long likeCount = Long.parseLong(likeCountStr);
                comment.setLikeCount(likeCount);
            }
            // 判断当前用户是否点赞该图片
            Boolean isLike = checkCommentLiked(CommentVO.objToVo(comment), loginUser);
            // 设置是否点赞状态
            comment.setIsLike(isLike);
        }
    }
}




